<?php
// @formatter:off
/**
 * @file WCCron.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: WCCron.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.1/includes/WCCron.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/WCCronTool.php";

/**
 * @class WCCron This class is the one to run and manage 'cronable' operations.
 */
class WCCron {
	/**
	 * @var WCCron This is the sole instance for this singleton class.
	 */
	protected static $_Instance;
	/**
	 * @var string When this class is set to perform only one cron operation,
	 * this variable will contain the identifier of that operation.
	 */
	protected $_singleTool = false;
	/**
	 * @var string When this class is set to perform only one set of cron
	 * operation, this variable will contain the identifier of that
	 * operation's set.
	 */
	protected $_profile = false;
	/**
	 * @var boolean This flag indicates if the cron operation should prompt
	 * its help message instead of being run.
	 * Also, if there are no cron operation specified, this class prompts its
	 * own help message.
	 */
	protected $_showHelp = false;
	/**
	 * @var boolean This flag indicates if time statistics must be shown or
	 * not.
	 */
	protected $_showTimers = false;
	/**
	 * Class basic constructor.
	 */
	protected function __construct() {
		//
		// By default, no specific operation is assumed.
		$this->_singleTool = false;
		//
		// Unless it is specified, help message should not be shown.
		$this->_showHelp = false;
	}
	/**
	 * Class basic destructor.
	 */
	public function __destruct() {
		//
		// This class has nothing to clean before deleting.
	}
	/**
	 * Prevent users from clone the singleton's instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	//
	// Public methods.
	/**
	 * This method triggers the execution of all the specified cron
	 * operations.
	 *
	 * @param string $spacer Since this method prompts text, this parameter
	 * indicates the left margin to be used.
	 */
	public function run($spacer = "") {
		//
		// Creating a shortcut to timers.
		$timer = WCTimer::Instance();
		//
		// Loading parameters and analyzing them.
		$this->loadParams();
		//
		// Checking if this call was a help request.
		if($this->_showHelp) {
			//
			// Prompting help messages.
			$this->showHelp();
		} else {
			//
			// Loading global settings.
			// @{
			global $wcCronScripts;
			global $wcCronProfiles;
			global $wcCronProfile;
			global $wcOptions;
			// @}
			//
			// Asking for an suitable log.
			$log = WCLog::Get(WC_LOGNAME_CRON);
			//
			// Starting log.
			$log->set();
			//
			// Creating a list for all cron operation identifiers
			// to be used.
			$availableIds = array();
			//
			// Checking that there is a specific cron operation set.
			if($this->_singleTool !== false) {
				//
				// Checking that the specified operation is valid.
				if($this->validateTool($this->_singleTool)) {
					//
					// Adding the operation to the list.
					$availableIds[] = $this->_singleTool;
				} else {
					//
					// If it's not valid, a error message is
					// shown.
					$log->write($spacer.WCColorText::ShellRed("Error").": Configuration for ID '{$this->_singleTool}' seems to be invalid\n", WCLogLevel::Error);
				}
			} elseif(!isset($wcCronProfiles[$this->_profile])) {
				//
				// When there is no specific cron operation set
				// and the profile set is not present, an error
				// message is shown.
				$log->write($spacer.WCColorText::ShellRed("Error").": Configuration for profile ID '{$this->_profile}' doesn't exist.\n", WCLogLevel::Error);
			} else {
				//
				// When there is no specific cron operation set
				// and the profile set is present, all its cron
				// operation identifiers are added to the list.
				foreach($wcCronProfiles[$this->_profile] as $id) {
					//
					// Checking that the id is present in the
					// list of cron operations.
					if(!isset($wcCronScripts[$id])) {
						$log->write($spacer.WCColorText::ShellRed("Error").": ID '{$id}' is not a valid id\n", WCLogLevel::Fatal);
						die ;
					}
					//
					// Creating a shortcut to the cron
					// operation.
					$cron = $wcCronScripts[$id];
					//
					// Checking that the cron operation is:
					//	- a valid cron operation.
					//	- auto executable
					//	- enabled to be run.
					if($this->validateTool($id) && $cron["auto"] && $cron["enabled"]) {
						//
						// Checking if there are automatic
						// parameters set for this cron
						// operation.
						if(isset($cron["params"])) {
							//
							// if this property is not
							// an array it is an error
							// and a message is shown.
							if(!is_array($cron["params"])) {
								$log->write($spacer.WCColorText::ShellRed("Error").": Parameters configured for ID '{$id}' seem to be invalid\n", WCLogLevel::Fatal);
								die ;
							}
						}
						//
						// At this point, it is ok to add
						// the id to the list.
						$availableIds[] = $id;
					} elseif(!$this->validateTool($id)) {
						//
						// If the cron operation is not
						// valid an error message is shown.
						$log->write($spacer.WCColorText::ShellYellow("Warning").": Configuration for ID '{$id}' seems to be invalid", WCLogLevel::Warning);
						//
						// Checking if it's possible to
						// show its name in the message.
						if(isset($cron["name"])) {
							$log->write(" (name: '{$cron["name"]}')\n");
						} else {
							$log->write("\n");
						}
					} elseif(!$cron["enabled"]) {
						//
						// If the cron operation is not
						// enabled, a warning message is
						// shown.
						$log->write($spacer.WCColorText::ShellYellow("Warning").": Configuration for ID '{$id}' seems to be disabled", WCLogLevel::Warning);
						//
						// Checking if it's possible to
						// show its name in the message.
						if(isset($cron["name"])) {
							$log->write(" (name: '{$cron["name"]}')\n");
						} else {
							$log->write("\n");
						}
					} elseif(!$cron["auto"]) {
						//
						// If the cron operation is not
						// automatic operation it must be
						// run directly. Therefore, a
						// warning message is shown.
						$log->write($spacer.WCColorText::ShellYellow("Warning").": Configuration for ID '{$id}' belongs to a not automatic tool", WCLogLevel::Warning);
						//
						// Checking if it's possible to
						// show its name in the message.
						if(isset($cron["name"])) {
							$log->write(" (name: '{$cron["name"]}')\n");
						} else {
							$log->write("\n");
						}
					}
				}
			}
			//
			// Loading all required class based on the previously
			// built list.
			foreach($availableIds as $id) {
				//
				// Checking if the class show be brought from a
				// PHP file.
				if(isset($wcCronScripts[$id]["path"])) {
					//
					// Loading the file containing the class.
					require_once $wcCronScripts[$id]["path"];
				}
				//
				// Checking that the required class exists.
				// If it doesn't an exception in raised.
				if(!class_exists($wcCronScripts[$id]["class"])) {
					$log->write($spacer.WCColorText::ShellRed("Error").": Class '{$wcCronScripts[$id]["class"]}' for tool '{$id}' does not exists.\n", WCLogLevel::Fatal);
					die ;
				}
			}
			//
			// Setting a timer ID.
			$timerId = "CRON";
			//
			// Running cron operations based on the previously built
			// list.
			foreach($availableIds as $id) {
				//
				// Starting timer for current tool.
				$timer->start($timerId);
				//
				// Cleaning all previously set and analyzed
				// options.
				$wcOptions->clear();
				//
				// Checking tool main class existence.
				if(!class_exists($wcCronScripts[$id]["class"])) {
					$log->write("{$spacer}".WCColorText::ShellRed("Error").": Class '{$wcCronScripts[$id]["class"]}' for tool '{$id}' does not exists.\n", WCLogLevel::Fatal);
					die ;
				}
				//
				// Prompting what is going to be run.
				$log->write($spacer.WCColorText::ShellGreen($wcCronScripts[$id]["name"].':')."\n", WCLogLevel::Info);
				//
				// Creating an object to manage the current cron
				// operation.
				$tool = new $wcCronScripts[$id]["class"]();
				//
				// If there wasn't a specific cron operation to be
				// run, there may be automatic parameters that
				// must be analyzed.
				if($this->_singleTool === false && isset($wcCronScripts[$id]["params"])) {
					// Analyzing automatic parameters.
					$tool->checkParams($wcCronScripts[$id]["params"]);
				}
				//
				// Actually running the cron operation.
				$tool->run("{$spacer}\t");
				//
				// Saving time statistics.
				$timer->save($timerId, "CRON", $id);
				//
				// Prompting stats.
				if($this->_showTimers) {
					$log->write($spacer.WCColorText::ShellGreen($wcCronScripts[$id]["name"].':')." took: {$timer->timer($timerId)} ms\n", WCLogLevel::Info);
				}
			}
		}
	}
	/**
	 * This method prompt the help message.
	 *
	 * @param string $spacer Since this method prompts text, this parameter
	 * indicates the left margin to be used.
	 */
	public function showHelp($spacer = "") {
		//
		// Loading required global pointers.
		// @{
		global $wcCronScripts;
		global $wcCronProfiles;
		global $wcCronProfile;
		// @}
		//
		// Checking if there is a specific cron operation set.
		if($this->_singleTool !== false) {
			//
			// Checking if the specific cron operation is a valid one.
			if($this->validateTool($this->_singleTool)) {
				//
				// Prompting what is going to be shown.
				echo $spacer.WCColorText::ShellGreen($wcCronScripts[$this->_singleTool]["name"].":")."\n";
				//
				// Checking if it needs to load a file.
				if(isset($wcCronScripts[$this->_singleTool]["path"])) {
					//
					// Loading the required file.
					require_once $wcCronScripts[$this->_singleTool]["path"];
				}
				//
				// Creating an object to manage the operation.
				$tool = new $wcCronScripts[$this->_singleTool]["class"]();
				//
				// Showing the operation's help message.
				$tool->showHelp("{$spacer}\t");
				//
				// Freeing memory.
				unset($tool);
			} else {
				//
				// If it's not valid, an error message is shown.
				$log->write(WCColorText::ShellRed("Error").": There is no tool with id '{$this->_singleTool}' or maybe it is invalid.\n", WCLogLevel::Error);
			}
		} else {
			//
			// When there is no specific cron operation set, main help
			// message must be shown.
			// @{
			echo "Usage:\n";
			echo "\tphp cron.php [options]\n";

			echo "\nWhen there are no options given, this script will try to run all configured tools.\n";

			echo "\nOptions:\n";
			echo "\t-h, --help\n";
			echo "\t\tShows this help text.\n\n";
			echo "\t-P id, --tools-profile id\n";
			echo "\t\tRuns the specified profile. See 'Available Profiles' below.\n";
			echo "\t-T id, --single-tool id\n";
			echo "\t\tRuns the specified tool. See 'Available Tools' below.\n";
			echo "\t--timers\n";
			echo "\t\tActivates timers prompts.\n";
			// @}
			//
			// Prompting available cron operations.
			// @{
			echo "\nAvailable Tools:\n";
			//
			// Prompting automatic cron operatios.
			//	@{
			echo "\tAutomatic Tools:\n";
			foreach($wcCronScripts as $id => $tool) {
				if($this->validateTool($id) && $tool['auto']) {
					echo "\t\t- {$id}:\n\t\t\t{$tool['name']}".(!$wcCronScripts[$id]['enabled'] ? " (".WCColorText::ShellYellow("Disabled").")" : "")."\n";
				}
			}
			//	@}
			//
			// Prompting not automatic cron operations.
			//	@{
			echo "\tNot Automatic Tools:\n";
			foreach($wcCronScripts as $id => $tool) {
				if($this->validateTool($id) && !$tool['auto']) {
					echo "\t\t- {$id}:\n\t\t\t{$tool['name']}".(!$wcCronScripts[$id]['enabled'] ? " (".WCColorText::ShellYellow("Disabled").")" : "")."\n";
				}
			}
			//	@}
			// @}
			//
			// Prompting available cron profiles.
			// @{
			echo "\nAvailable Profiles:\n";
			foreach($wcCronProfiles as $name => $list) {
				echo "\t- {$name}\n";
				//
				// Prompting its cron operation in the configured
				// order.
				// @{
				$i = 1;
				foreach($list as $item) {
					echo "\t\t{$i}: {$item}\n";
					$i++;
				}
				// @}
			}
			// @}
		}
	}
	//
	// Protected methods.
	/**
	 * This method checks if certain cron operation is set.
	 *
	 * @param string $id Cron operation identifier.
	 * @return boolean Returns true when cron operation is set.
	 */
	protected function hasTool($id) {
		//
		// Loading the global list of cron operations.
		global $wcCronScripts;
		//
		// Returning whether it is set or not.
		return isset($wcCronScripts[$id]);
	}
	/**
	 * This methods loads parameters and flags sets that may be needed for
	 * execution.
	 */
	public function loadParams() {
		//
		// Loading global pointers.
		// @{
		global $wcCronProfile;
		global $wcOptions;
		// @}
		//
		// Setting help option:
		//	-h
		//	--help
		if($wcOptions->addOption("Help")) {
			$wcOptions->addOptionTrigger("Help", "h");
			$wcOptions->addOptionTrigger("Help", "help");
		}
		//
		// Setting option to specify certain cron operation:
		//	-T
		//	--single-tool
		if($wcOptions->addValueOption("SingleTool")) {
			$wcOptions->addOptionTrigger("SingleTool", "T");
			$wcOptions->addOptionTrigger("SingleTool", "single-tool");
		}
		//
		// Activating timers:
		//	--timers
		if($wcOptions->addOption("Timers")) {
			$wcOptions->addOptionTrigger("Timers", "timers");
		}
		//
		// Setting option to specify certain cron profile:
		//	-P
		//	--tools-profile
		if($wcOptions->addValueOption("ToolsProfile")) {
			$wcOptions->addOptionTrigger("ToolsProfile", "P");
			$wcOptions->addOptionTrigger("ToolsProfile", "tools-profile");
		}
		//
		// Triggering the options analysis.
		$wcOptions->check();
		//
		// Checking if certain cron operation was specified.
		if($wcOptions->activated("SingleTool")) {
			//
			// Setting the specified cron operation.
			$this->_singleTool = $wcOptions->value("SingleTool");
		}
		//
		// Checking if certain cron profile was specified.
		if($wcOptions->activated("ToolsProfile")) {
			//
			// Setting the specified profile.
			$this->_profile = $wcOptions->value("ToolsProfile");
		} else {
			//
			// if there were no profile specified, the default
			// configured profile is set.
			$this->_profile = $wcCronProfile;
		}
		//
		// Checking if help was requested.
		if($wcOptions->activated("Help")) {
			$this->_showHelp = true;
		}
		//
		// Checking if time statistics has to be shown.
		if($wcOptions->activated("Timers")) {
			$this->_showTimers = true;
		}
	}
	/**
	 * This method forces a user login using the cron user id.
	 * This is the reason why the cron user password has no real meaning.
	 */
	protected function login() {
		//
		// Loading global pointers.
		// @{
		global $wcUser;
		global $wcSession;
		// @}
		//
		// Creating a new user to manage the session.
		$wcUser = new WCUser();
		//
		// Loading cron user.
		$wcUser->load(WC_USER_CRON_ID);
		//
		// Starting session.
		$wcSession->login(WC_USER_CRON_ID);
	}
	/**
	 * This method logs out the cron user.
	 */
	protected function logout() {
		//
		// Loading global pointers.
		// @{
		global $wcUser;
		global $wcSession;
		// @}
		//
		// Clearing the user pointer.
		$wcUser = false;
		//
		// Closing session.
		$wcSession->logout();
	}
	/**
	 * This method checks if certain cron operation exists and it is well
	 * specified.
	 *
	 * @param string $id Cron operation identifier.
	 * @return boolean Returns true when cron operation is set and valid.
	 */
	protected function validateTool($id) {
		/** @todo UPDATE */
		//
		// Loading the global list of cron operations.
		global $wcCronScripts;
		//
		// This is the default value to be returned.
		$valid = true;
		//
		// Checking that the cron operation is set.
		if(!$this->hasTool($id)) {
			$valid = false;
		} else {
			//
			// At this point, it is considered that it is set.
			//
			// Checking that it has a class specified.
			if(!isset($wcCronScripts[$id]["class"])) {
				$valid = false;
			}
			//
			// Checking that it has an enable status specified.
			if(!isset($wcCronScripts[$id]["enabled"])) {
				$valid = false;
			}
			//
			// Checking that it has an automatic status specified.
			if(!isset($wcCronScripts[$id]["auto"])) {
				$valid = false;
			}
			//
			// If there is a path specified, it is checks to be a
			// readable file.
			if(isset($wcCronScripts[$id]["path"]) && !is_readable($wcCronScripts[$id]["path"])) {
				$valid = false;
			}
		}
		//
		// Returning if its valid or not.
		return $valid;
	}
	//
	// Public class methods.
	/**
	 * This class-method creates and returns the sole instance for this
	 * singleton class.
	 *
	 * @return WCCron Singleton instance.
	 */
	public static function Instance() {
		//
		// Checking if there's no instance yet.
		if(!isset(self::$_Instance)) {
			$c = __CLASS__;
			//
			// Creating the sole instance.
			self::$_Instance = new $c();
		}
		//
		// Returning the class instance.
		return self::$_Instance;
	}
}
?>